home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 97 / CD-ROM 97 / CD-ROM 97.iso / internet / ghostzilla / ghsetup.exe / chrome / comm.jar / content / navigator / pageInfo.js < prev    next >
Encoding:
JavaScript  |  2002-04-20  |  31.5 KB  |  1,018 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public
  6.  * License Version 1.1 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is Netscape
  18.  * Communications Corporation.  Portions created by Netscape are
  19.  * Copyright (C) 1998 Netscape Communications Corporation. All
  20.  * Rights Reserved.
  21.  *
  22.  * Contributor(s): smorrison@gte.com
  23.  *   Terry Hayes <thayes@netscape.com>
  24.  *   Daniel Brooks <db48x@yahoo.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the NPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the NPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK *****
  39. */
  40.  
  41. // mmm, yummy. global variables.
  42. var theWindow = null;
  43. var theDocument = null;
  44.  
  45. var linkList = new Array();
  46. var formList = new Array();
  47. var imageList = new Array();
  48.  
  49. var linkIndex = 0;
  50. var formIndex = 0;
  51. var imageIndex = 0;
  52. var frameCount = 0;
  53.  
  54. const COPYCOL_NONE = -1;
  55. const COPYCOL_META_CONTENT = 1;
  56. const COPYCOL_FORM_ACTION = 3;
  57. const COPYCOL_LINK_ADDRESS = 2;
  58. const COPYCOL_IMAGE_ADDRESS = 1;
  59.  
  60. // a number of services I'll need later
  61. // the cache services
  62. const nsICacheService = Components.interfaces.nsICacheService;
  63. const cacheService = Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
  64. var httpCacheSession = cacheService.createSession("HTTP", 0, true);
  65. var ftpCacheSession = cacheService.createSession("FTP", 0, true);
  66.  
  67. // scriptable date formater, for pretty printing dates
  68. const nsIScriptableDateFormat = Components.interfaces.nsIScriptableDateFormat;
  69. var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"].getService(nsIScriptableDateFormat);
  70.  
  71. // clipboard helper
  72. try
  73. {
  74.   const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
  75. }
  76. catch(e)
  77. {
  78.   // do nothing, later code will handle the error
  79. }
  80.  
  81. // namespaces, don't need all of these yet...
  82. const XLinkNS = "http://www.w3.org/1999/xlink";
  83. const XULNS   = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  84. const XMLNS   = "http://www.w3.org/XML/1998/namespace";
  85. const XHTMLNS = "http://www.w3.org/1999/xhtml";
  86.  
  87. /* Overlays register init functions here.
  88.  *   Add functions to call by invoking "onLoadRegistry.append(XXXLoadFunc);"
  89.  *   The XXXLoadFunc should be unique to the overlay module, and will be
  90.  *   invoked as "XXXLoadFunc();"
  91.  */
  92. var onLoadRegistry = [ ];
  93.  
  94. /* Called when PageInfo window is loaded.  Arguments are:
  95.  *  window.arguments[0] - document to use for source (null=Page Info, otherwise Frame Info)
  96.  *  window.arguments[1] - tab name to display first (may be null)
  97. */
  98. function onLoadPageInfo()
  99. {
  100.   //dump("===============================================================================\n");
  101.   var theBundle = document.getElementById("pageinfobundle");
  102.   var unknown = theBundle.getString("unknown");
  103.  
  104.   var docTitle = "";
  105.   if("arguments" in window && window.arguments.length >= 1 && window.arguments[0])
  106.   {
  107.     theWindow = null;
  108.     theDocument = window.arguments[0];
  109.     docTitle = theBundle.getString("frameInfo.title");
  110.   } 
  111.   else 
  112.   {
  113.     if ("gBrowser" in window.opener)
  114.       theWindow = window.opener.gBrowser.contentWindow;
  115.     else
  116.       theWindow = window.opener.frames[0];
  117.     theDocument = theWindow.document;
  118.  
  119.     docTitle = theBundle.getString("pageInfo.title");
  120.   }
  121.  
  122.   document.title = docTitle;
  123.  
  124.   // do the easy stuff first
  125.   makeGeneralTab();
  126.  
  127.   /* Call registered overlay init functions */
  128.   for (x in onLoadRegistry)
  129.   {
  130.     onLoadRegistry[x]();
  131.   }
  132.  
  133.   /* Select the requested tab, if the name is specified */
  134.   if ("arguments" in window && window.arguments.length > 1)
  135.   {
  136.     var tabName = window.arguments[1];
  137.  
  138.     if (tabName)
  139.     {
  140.       var tabControl = document.getElementById("tabbox");
  141.       var tab = document.getElementById(tabName);
  142.  
  143.       if (tabControl && tab)
  144.       {
  145.         tabControl.selectedTab = tab;
  146.       }
  147.     }
  148.   }
  149. }
  150.  
  151. function makeGeneralTab()
  152. {
  153.   var theBundle = document.getElementById("pageinfobundle");
  154.   var unknown = theBundle.getString("unknown");
  155.   var notSet = theBundle.getString("notset");
  156.   
  157.   var title = (theDocument.title) ? theBundle.getFormattedString("pageTitle", [theDocument.title]) : theBundle.getString("noPageTitle");
  158.   document.getElementById("titletext").value = title;
  159.  
  160.   var url = theDocument.location;
  161.   document.getElementById("urltext").value = url;
  162.  
  163.   var mode = ("compatMode" in theDocument && theDocument.compatMode == "BackCompat") ? theBundle.getString("generalQuirksMode") : theBundle.getString("generalStrictMode");
  164.   document.getElementById("modetext").value = mode;
  165.  
  166.   // find out the mime type
  167.   var mimeType = theDocument.contentType || unknown;
  168.   document.getElementById("typetext").value = mimeType;
  169.   
  170.   // get the meta tags
  171.   var metaNodes = theDocument.getElementsByTagName("meta");
  172.   var metaTree = document.getElementById("metatree");
  173.   var metaView = new pageInfoTreeView(["meta-name","meta-content"], COPYCOL_META_CONTENT);
  174.  
  175.   metaTree.treeBoxObject.view = metaView;
  176.  
  177.   var length = metaNodes.length;
  178.   for (var i = 0; i < length; i++)
  179.   {    
  180.     metaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
  181.   }
  182.   metaView.rowCountChanged(0, length);
  183.   
  184.   // get the document characterset
  185.   var encoding = theDocument.characterSet;
  186.   document.getElementById("encodingtext").value = encoding;
  187.  
  188.   // get the date of last modification
  189.   var modifiedText = formatDate(theDocument.lastModified, notSet);
  190.   document.getElementById("modifiedtext").value = modifiedText;
  191.   
  192.   // get cache info
  193.   var sourceText = theBundle.getString("generalNotCached");
  194.   var expirationText = theBundle.getString("generalNoExpiration");
  195.   var sizeText = unknown;
  196.  
  197.   var pageSize = 0; 
  198.   var kbSize = 0;
  199.   var expirationTime = 0;
  200.  
  201.   try
  202.   {
  203.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  204.     if(cacheEntryDescriptor)
  205.     { 
  206.       switch(cacheEntryDescriptor.deviceID)
  207.       {
  208.         case "disk":
  209.           sourceText = theBundle.getString("generalDiskCache");
  210.           break;
  211.         case "memory":
  212.           sourceText = theBundle.getString("generalMemoryCache");
  213.           break;
  214.         default:
  215.           sourceText = cacheEntryDescriptor.deviceID;
  216.           break;
  217.       }
  218.  
  219.       pageSize = cacheEntryDescriptor.dataSize;
  220.       kbSize = pageSize / 1024;
  221.       sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  222.  
  223.       expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, notSet);
  224.     }
  225.   }
  226.   catch(ex)
  227.   {
  228.     try
  229.     {
  230.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  231.       if (cacheEntryDescriptor)
  232.       {
  233.         switch(cacheEntryDescriptor.deviceID)
  234.         {
  235.           case "disk":
  236.             sourceText = theBundle.getString("generalDiskCache");
  237.             break;
  238.           case "memory":
  239.             sourceText = theBundle.getString("generalMemoryCache");
  240.             break;
  241.           default:
  242.             sourceText = cacheEntryDescriptor.deviceID;
  243.             break;
  244.         }
  245.  
  246.         pageSize = cacheEntryDescriptor.dataSize;
  247.         kbSize = pageSize / 1024;
  248.         sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  249.  
  250.         expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, notSet);
  251.       }
  252.     }
  253.     catch(ex2)
  254.     {
  255.       sourceText = theBundle.getString("generalNotCached");
  256.     }
  257.   }
  258.   document.getElementById("sourcetext").value = sourceText;
  259.   document.getElementById("expirestext").value = expirationText;
  260.   document.getElementById("sizetext").value = sizeText;
  261. }
  262.  
  263. //******** Form Stuff
  264. function makeFormTab()
  265. {
  266.   var formTree = document.getElementById("formtree");
  267.   var formPreview = document.getElementById("formpreview");
  268.   
  269.   var formView = new pageInfoTreeView(["form-number","form-name","form-method","form-action"], COPYCOL_FORM_ACTION);
  270.   var fieldView = new pageInfoTreeView(["field-number","field-label","field-field","field-type","field-value"], COPYCOL_NONE);
  271.   formTree.treeBoxObject.view = formView;
  272.   formPreview.treeBoxObject.view = fieldView;
  273.   formList = grabAllForms(theWindow, theDocument);
  274.   formIndex = 0;
  275.  
  276.   var length = formList.length;
  277.   for (var i = 0; i < length; i++)
  278.   {
  279.     var elem = formList[i];
  280.     formView.addRow([++formIndex, elem.name, elem.method, elem.getAttribute("action")]);  // use getAttribute() because of bug 122128
  281.   }
  282.   formView.rowCountChanged(0, length);
  283.  
  284.   formView.selection.select(0);
  285. }
  286.  
  287. function grabAllForms(aWindow, aDocument)
  288. {
  289.   var theList = [];
  290.  
  291.   if (aWindow && aWindow.frames.length > 0)
  292.   {
  293.     var length = aWindow.frames.length;
  294.     for (var i = 0; i < length; i++)
  295.     {
  296.       var frame = aWindow.frames[i];
  297.       theList = theList.concat(grabAllForms(frame, frame.document));
  298.     }
  299.   }
  300.  
  301.   if ("forms" in aDocument)
  302.     return theList.concat(aDocument.forms);
  303.   else
  304.     return theList.concat(aDocument.getElementsByTagNameNS(XHTMLNS, "form"));
  305. }
  306.  
  307. function onFormSelect()
  308. {
  309.   var theBundle = document.getElementById("pageinfobundle");
  310.   var formTree = document.getElementById("formtree");
  311.   var formView = formTree.treeBoxObject.view;
  312.   if (!formView.rowCount) return;
  313.  
  314.   if (formView.selection.count == 1)
  315.   {
  316.     var formPreview = document.getElementById("formpreview");
  317.     var fieldView = new pageInfoTreeView(["field-number","field-label","field-field","field-type","field-value"], COPYCOL_NONE);
  318.     formPreview.treeBoxObject.view = fieldView;
  319.  
  320.     var clickedRow = formView.selection.currentIndex;
  321.     var formnum = formView.getCellText(clickedRow, "form-number");
  322.     var form = formList[formnum-1];
  323.     var ft = null;
  324.     if (form.name)
  325.       ft = theBundle.getFormattedString("formTitle", [form.name]);
  326.     else
  327.       ft = theBundle.getString("formUntitled");
  328.  
  329.     document.getElementById("formname").value = ft || theBundle.getString("formUntitled");
  330.     document.getElementById("formenctype").value = form.encoding || theBundle.getString("default");
  331.     document.getElementById("formtarget").value = form.target || theBundle.getString("formDefaultTarget");
  332.  
  333.     var formfields = form.elements;
  334.  
  335.     var length = formfields.length;
  336.     var i = 0;
  337.  
  338.     var checked = theBundle.getString("formChecked");
  339.     var unchecked = theBundle.getString("formUnchecked");    
  340.  
  341.     for (i = 0; i < length; i++)
  342.     {
  343.       var elem = formfields[i];
  344.  
  345.       if(elem.nodeName.toLowerCase() == "button")
  346.         fieldView.addRow([i+1, "", elem.name, elem.type, getValueText(elem)]);
  347.       else
  348.       {
  349.         var val = (elem.type == "password") ? theBundle.getString("formPassword") : elem.value;
  350.         fieldView.addRow([i+1, "", elem.name, elem.type, val]);
  351.       }
  352.     }
  353.  
  354.     var labels = form.getElementsByTagName("label");
  355.     var llength = labels.length
  356.  
  357.     for (i = 0; i < llength; i++)
  358.     {
  359.       var whatfor = labels[i].getAttribute("for") || findFirstControl(labels[i]);
  360.       var labeltext = getValueText(labels[i]);
  361.  
  362.       for(var j = 0; j < length; j++)
  363.         if (formfields[j] == whatfor || formfields[j].name == whatfor)
  364.           fieldView.setCellText(j, "field-label", labeltext);
  365.     }
  366.  
  367.     fieldView.rowCountChanged(0, length);
  368.   }
  369. }
  370.  
  371. function findFirstControl(node)
  372. {
  373.   function FormControlFilter() 
  374.   {
  375.     this.acceptNode = function(node)
  376.     {
  377.       switch (node.nodeName.toLowerCase())
  378.       {
  379.         case "input":
  380.         case "select":
  381.         case "button":
  382.         case "textarea":
  383.         case "object":
  384.           return NodeFilter.FILTER_ACCEPT;
  385.           break;
  386.         default:
  387.           return NodeFilter.FILTER_SKIP;
  388.           break;
  389.       }
  390.       return NodeFilter.FILTER_SKIP;   // placate the js compiler
  391.     }     
  392.   }
  393.  
  394.   var iterator = theDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, FormControlFilter, true);
  395.  
  396.   return iterator.nextNode();
  397. }
  398.  
  399. //******** Link Stuff
  400. function makeLinkTab()
  401. {
  402.   //var start = new Date();
  403.   var theBundle = document.getElementById("pageinfobundle");
  404.   var linkTree = document.getElementById("linktree");
  405.  
  406.   var linkView = new pageInfoTreeView(["link-number","link-name","link-address","link-type"], COPYCOL_LINK_ADDRESS);
  407.   linkTree.treeBoxObject.view = linkView;
  408.  
  409.   linkList = grabAllLinks(theWindow, theDocument);
  410.  
  411.   var linkAnchor = theBundle.getString("linkAnchor");
  412.   var linkArea = theBundle.getString("linkArea");
  413.   var linkSubmit = theBundle.getString("linkSubmit");
  414.   var linkSubmission = theBundle.getString("linkSubmission");
  415.   var linkRel = theBundle.getString("linkRel");
  416.   var linkStylesheet = theBundle.getString("linkStylesheet");
  417.   var linkRev = theBundle.getString("linkRev");
  418.   var linkX = theBundle.getString("linkX");
  419.  
  420.   var linktext = null;
  421.   linkIndex = 0;
  422.  
  423.   var length = linkList.length;
  424.   for (var i = 0; i < length; i++)
  425.   {
  426.     var elem = linkList[i];
  427.     switch (elem.nodeName.toLowerCase())
  428.     {
  429.       case "a":
  430.         linktext = getValueText(elem);
  431.         linkView.addRow([++linkIndex, linktext, elem.href, linkAnchor]);
  432.         break;
  433.       case "area":
  434.         linkView.addRow([++linkIndex, elem.alt, elem.href, linkArea]);
  435.         break;
  436.       case "input":
  437.         linkView.addRow([++linkIndex, elem.value || linkSubmit, elem.form.getAttribute("action"), linkSubmission]); // use getAttribute() due to bug 122128
  438.         break;
  439.       case "link":
  440.         if (elem.rel)
  441.         {
  442.           // should this test use regexes to be a little more lenient wrt whitespace?
  443.           if (elem.rel.toLowerCase() == "stylesheet" || elem.rel.toLowerCase() == "alternate stylesheet")
  444.             linktext = linkStylesheet;
  445.           else
  446.             linktext = linkRel;
  447.         }
  448.         else
  449.           linktext = linkRev;
  450.         linkView.addRow([++linkIndex, elem.rel || elem.rev, elem.href, linktext]);
  451.         break;
  452.       default:
  453.         if (elem.hasAttributeNS(XLinkNS, "href"))
  454.         {
  455.           linktext = getValueText(elem);
  456.           linkView.AddRow([++linkIndex, linktext, elem.href, linkX]);
  457.         }
  458.         else
  459.           dump("Page Info - makeLinkTab(): Hey, that's an odd one! ("+elem+")");
  460.         break;
  461.     }
  462.   }
  463.   linkView.rowCountChanged(0, length);
  464.  
  465.   //var end = new Date();
  466.   //dump("links tab took "+(end-start)+"ms to build.\n");
  467. }
  468.  
  469. function grabAllLinks(aWindow,aDocument)
  470. {
  471.   var theList = [];
  472.  
  473.   if (aWindow && aWindow.frames.length > 0)
  474.   {
  475.     var num = aWindow.frames.length;
  476.     for (var i = 0; i < num; i++)
  477.     {
  478.       var frame = aWindow.frames[i];
  479.       theList = theList.concat(grabAllLinks(frame, frame.document));
  480.     }
  481.   }
  482.  
  483.   theList = theList.concat(aDocument.getElementsByTagName("link"));
  484.  
  485.   var inputList = aDocument.getElementsByTagName("input");
  486.   var length = inputList.length;
  487.   for (i = 0; i < length; i++)
  488.     if (inputList[i].type.toLowerCase() == "submit")
  489.       theList = theList.concat(inputList[i]);
  490.  
  491.   theList = theList.concat(grabAllXLinks(aDocument));
  492.  
  493.   if ("links" in aDocument)
  494.     return theList.concat(aDocument.links);
  495.   else
  496.     return theList.concat(aDocument.getElementsByTagNameNS(XHTMLNS, "a"));
  497. }
  498.  
  499. function grabAllXLinks(aDocument)
  500. {
  501.   function XLinkFilter() 
  502.   {
  503.     this.acceptNode = function(aDocument)
  504.     {
  505.       return (aDocument.hasAttributeNS(XLinkNS, "href")) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
  506.     }     
  507.   }
  508.  
  509.   var nodeFilter = new XLinkFilter;
  510.   var iterator = aDocument.createTreeWalker(aDocument, NodeFilter.SHOW_ELEMENT, nodeFilter, true);
  511. // why doesn't this work? the same thing works in findFirstControl() :P
  512. //  var iterator = aDocument.createTreeWalker(aDocument, NodeFilter.SHOW_ELEMENT, XLinkFilter, true);
  513.  
  514.   var theList = new Array();
  515.  
  516.   while(iterator.nextNode())
  517.     theList = theList.concat(iterator.currentNode);
  518.  
  519.   return theList;
  520. }
  521.  
  522.  
  523. function openURL(target)
  524. {
  525.   var url = target.parentNode.childNodes[2].value;
  526.   window.open(url, "_blank", "chrome");
  527. }
  528.  
  529. //******** Image Stuff
  530. function makeMediaTab()
  531. {
  532.   var theBundle = document.getElementById("pageinfobundle");
  533.   var imageTree = document.getElementById("imagetree");
  534.  
  535.   var imageView = new pageInfoTreeView(["image-number","image-address","image-type"], COPYCOL_IMAGE_ADDRESS);
  536.   imageTree.treeBoxObject.view = imageView;
  537.  
  538.   imageList = grabAllMedia(theWindow, theDocument);
  539.  
  540.   var mediaImg = theBundle.getString("mediaImg");
  541.   var mediaApplet = theBundle.getString("mediaApplet");
  542.   var mediaObject = theBundle.getString("mediaObject");
  543.   var mediaEmbed = theBundle.getString("mediaEmbed");
  544.   var mediaLink = theBundle.getString("mediaLink");
  545.   var mediaInput = theBundle.getString("mediaInput");
  546.  
  547.   var row = null;
  548.   var length = imageList.length;
  549.   imageIndex = 0;
  550.  
  551.   for (var i = 0; i < length; i++)
  552.   {
  553.     var elem = imageList[i];
  554.     switch (elem.nodeName.toLowerCase())
  555.     {
  556.       case "img":
  557.         imageView.addRow([++imageIndex, elem.src, mediaImg]);
  558.         break;
  559.       case "input":
  560.         imageView.addRow([++imageIndex, elem.src, mediaInput]);
  561.         break;
  562.       case "applet":
  563.         imageView.addRow([++imageIndex, elem.code || elem.object, mediaApplet]);
  564.         break;
  565.       case "object":
  566.         imageView.addRow([++imageIndex, elem.data, mediaObject]);
  567.         break;
  568.       case "embed":
  569.         imageView.addRow([++imageIndex, elem.src, mediaEmbed]);
  570.         break;
  571.       case "link":
  572.         imageView.addRow([++imageIndex, elem.href, mediaLink]);
  573.         break;
  574.       default:
  575.         dump("Page Info - makeMediaTab(): hey, that's an odd one! ("+elem+")");;
  576.         break;
  577.     }
  578.   }
  579.   imageView.rowCountChanged(0, length);
  580.   
  581.   imageView.selection.select(0);
  582. }
  583.  
  584. function grabAllMedia(aWindow, aDocument)
  585. {
  586.   var theList = [];
  587.  
  588.   if (aWindow && aWindow.frames.length > 0)
  589.   {
  590.     var num = aWindow.frames.length;
  591.     for (var i = 0; i < num; i++)
  592.     {
  593.       var frame = aWindow.frames[i];
  594.       theList = theList.concat(grabAllMedia(frame, frame.document));
  595.     }
  596.   }
  597.  
  598.   theList = theList.concat(aDocument.getElementsByTagName("embed"), aDocument.applets, aDocument.getElementsByTagName("object"));
  599.  
  600.   var inputList = aDocument.getElementsByTagName("input");
  601.   var length = inputList.length
  602.   for (i = 0; i < length; i++)
  603.     if(inputList[i].type.toLowerCase() == "image")
  604.       theList = theList.concat(inputList[i]);
  605.  
  606.   var linkList = aDocument.getElementsByTagName("link");
  607.   length = linkList.length;
  608.   for (i = 0; i < length; i++)
  609.     if(linkList[i].rel.match(/\bicon\b/i))
  610.       theList = theList.concat(linkList[i]);
  611.  
  612.   if ("images" in aDocument)
  613.     return theList.concat(aDocument.images);
  614.   else
  615.     return theList.concat(aDocument.getElementsByTagNameNS(XHTMLNS, "img"));
  616. }  
  617.  
  618. function getSource( item )
  619. {
  620.   // Return the correct source without strict warnings
  621.   if (item.href != null) {
  622.     return item.href;
  623.   } else if (item.src != null) {
  624.     return item.src;
  625.   }
  626.   return null;
  627. }
  628.  
  629. function getSelectedItem(tree)
  630. {
  631.   var view = tree.treeBoxObject.view;
  632.   if (!view.rowCount) return null;
  633.  
  634.   // Only works if only one item is selected
  635.   var clickedRow = tree.treeBoxObject.selection.currentIndex;
  636.   var lineNum = view.getCellText(clickedRow, "image-number");
  637.   return imageList[lineNum - 1];
  638. }
  639.  
  640. function saveMedia()
  641. {
  642.   var tree = document.getElementById("imagetree");
  643.   var item = getSelectedItem(tree);
  644.   var url = getAbsoluteURL(getSource(item), item);
  645.  
  646.   if (url) {
  647.     saveURL(url, null, 'SaveImageTitle', false );
  648.   }
  649. }
  650.  
  651. function onImageSelect()
  652. {
  653.   var tree = document.getElementById("imagetree");
  654.   var saveAsButton = document.getElementById("imagesaveasbutton");
  655.  
  656.   if (tree.treeBoxObject.selection.count == 1)
  657.   {
  658.     makePreview(getSelectedItem(tree));
  659.     saveAsButton.setAttribute("disabled", "false");
  660.   } else {
  661.     saveAsButton.setAttribute("disabled", "true");
  662.   }
  663. }
  664.  
  665. function makePreview(item)
  666. {
  667.   var theBundle = document.getElementById("pageinfobundle");
  668.   var unknown = theBundle.getString("unknown");
  669.   var notSet = theBundle.getString("notset");
  670.  
  671.   var url = ("src" in item && item.src) || ("code" in item && item.code) || ("data" in item && item.data) || ("href" in item && item.href) || unknown;  // it better have at least one of those...
  672.   document.getElementById("imageurltext").value = url;
  673.   document.getElementById("imagetitletext").value = item.title || notSet;
  674.   document.getElementById("imagealttext").value = ("alt" in item && item.alt) || getValueText(item) || notSet;
  675.   document.getElementById("imagelongdesctext").value = ("longDesc" in item && item.longDesc) || notSet;
  676.  
  677.   // find out the mime type
  678.   var mimeType = unknown;
  679.   if (item.nodeName.toLowerCase() != "input")
  680.     mimeType = ("type" in item && item.type) || ("codeType" in item && item.codeType) || ("contentType" in item && item.contentType) || unknown;
  681.   document.getElementById("imagetypetext").value = mimeType;
  682.  
  683.   // get cache info
  684.   var sourceText = theBundle.getString("generalNotCached");
  685.   var expirationText = theBundle.getString("unknown");
  686.   var sizeText = theBundle.getString("unknown");
  687.  
  688.   var expirationTime = 0;
  689.   var expirationDate = null;
  690.  
  691.   try
  692.   {
  693.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  694.     if (cacheEntryDescriptor)
  695.     {
  696.       switch(cacheEntryDescriptor.deviceID)
  697.       {
  698.         case "disk":
  699.           sourceText = theBundle.getString("generalDiskCache");
  700.           break;
  701.         case "memory":
  702.           sourceText = theBundle.getString("generalMemoryCache");
  703.           break;
  704.         default:
  705.           sourceText = cacheEntryDescriptor.deviceID;
  706.           break;
  707.       }
  708.  
  709.       pageSize = cacheEntryDescriptor.dataSize;
  710.       kbSize = pageSize / 1024;
  711.       sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  712.  
  713.       expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, notSet);
  714.     }
  715.   }
  716.   catch(ex)
  717.   {
  718.     try
  719.     {
  720.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  721.       if (cacheEntryDescriptor)
  722.       {
  723.         switch(cacheEntryDescriptor.deviceID)
  724.         {
  725.           case "disk":
  726.             sourceText = theBundle.getString("generalDiskCache");
  727.             break;
  728.           case "memory":
  729.             sourceText = theBundle.getString("generalMemoryCache");
  730.             break;
  731.           default:
  732.             sourceText = cacheEntryDescriptor.deviceID;
  733.             break;
  734.         }
  735.  
  736.         pageSize = cacheEntryDescriptor.dataSize;
  737.         kbSize = pageSize / 1024;
  738.         sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  739.  
  740.         expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, notSet);
  741.       }
  742.     }
  743.     catch(ex2)
  744.     {
  745.       sourceText = theBundle.getString("generalNotCached");
  746.     }
  747.   }
  748.   document.getElementById("imagesourcetext").value = sourceText;
  749.   document.getElementById("imageexpirestext").value = expirationText;
  750.   document.getElementById("imagesizetext").value = sizeText;
  751.  
  752.   var width = ("width" in item && item.width) || "";
  753.   var height = ("height" in item && item.height) || "";
  754.   document.getElementById("imagewidth").value = theBundle.getFormattedString("mediaWidth", [width]);
  755.   document.getElementById("imageheight").value = theBundle.getFormattedString("mediaHeight", [height]);
  756.  
  757.   var imageContainer = document.getElementById("theimagecontainer");
  758.   var oldImage = document.getElementById("thepreviewimage");
  759.  
  760.   var newImage = null;
  761.   var nn = item.nodeName.toLowerCase();
  762.   if (nn == "link" || nn == "input")
  763.   {
  764.     newImage = new Image();
  765.     newImage.src = getAbsoluteURL(getSource(item), item);
  766.   }
  767.   else
  768.   {
  769.     newImage = item.cloneNode(true);
  770.     newImage.src = ("src" in item && item.src) || ("href" in item && item.href);  // weird funky hack, I know :P
  771.   }
  772.  
  773.   newImage.setAttribute("id", "thepreviewimage");
  774.   if ("width" in item && item.width)
  775.     newImage.width = item.width;
  776.   if ("height" in item && item.height)
  777.     newImage.height = item.height;
  778.   newImage.removeAttribute("align"); // just in case.
  779.  
  780.   imageContainer.removeChild(oldImage);
  781.   imageContainer.appendChild(newImage);
  782. }
  783.  
  784.  
  785. //******** Other Misc Stuff
  786. // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  787. // parse a node to extract the contents of the node
  788. // linkNode doesn't really _have_ to be link
  789. function getValueText(linkNode)
  790. {
  791.   var valueText = "";
  792.   
  793.   var length = linkNode.childNodes.length;
  794.   for (var i = 0; i < length; i++)
  795.   {
  796.     var childNode = linkNode.childNodes[i];
  797.     var nodeType = childNode.nodeType;
  798.     if (nodeType == Node.TEXT_NODE)
  799.       valueText += " " + childNode.nodeValue;
  800.     else if (nodeType == Node.ELEMENT_NODE)
  801.     {
  802.       if (childNode.nodeName.toLowerCase() == "img")
  803.         valueText += " " + getAltText(childNode);
  804.       else
  805.         valueText += " " + getValueText(childNode);
  806.     }
  807.   }
  808.  
  809.   return stripWS(valueText);
  810. }
  811.  
  812. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  813. // traverse the tree in search of an img or area element and grab its alt tag
  814. function getAltText(node)
  815. {
  816.   var altText = "";
  817.   
  818.   if (node.alt)
  819.     return node.alt;
  820.   var length = node.childNodes.length;
  821.   for (var i = 0; i < length; i++)
  822.     if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
  823.       return altText;
  824.   return "";
  825. }
  826.  
  827. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  828. // strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
  829. function stripWS(text)
  830. {
  831.   var middleRE = /\s+/g;
  832.   var endRE = /(^\s+)|(\s+$)/g;
  833.  
  834.   text = text.replace(middleRE, " ");
  835.   return text.replace(endRE, "");
  836. }
  837.  
  838. function formatDate(datestr, unknown)
  839. {
  840.   var date = new Date(datestr);
  841.   return (date.valueOf()) ? dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatSeconds, date.getFullYear(), date.getMonth()+1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()) : unknown;
  842. }
  843.  
  844. /*
  845.  * Takes care of XMLBase and <base>
  846.  * url is the possibly relative url.
  847.  * node is the node where the url was given (needed for XMLBase)
  848.  *
  849.  * This function is called in many places as a workaround for bug 72524
  850.  * Once bug 72522 is fixed this code should use the Node.baseURI attribute
  851.  *
  852.  * for node==null or url=="", empty string is returned
  853.  *
  854.  * This is basically just copied from http://lxr.mozilla.org/seamonkey/source/xpfe/browser/resources/content/metadata.js
  855.  */
  856.  
  857. function getAbsoluteURL(url, node)
  858. {
  859.   if (!url || !node)
  860.     return "";
  861.   var urlArr = new Array(url);
  862.  
  863.   var doc = node.ownerDocument;
  864.   if (node.nodeType == Node.ATTRIBUTE_NODE)
  865.     node = node.ownerElement;
  866.  
  867.   while (node && node.nodeType == Node.ELEMENT_NODE) 
  868.   {
  869.     var att = node.getAttributeNS(XMLNS, "base");
  870.     if (att != "")
  871.       urlArr.unshift(att);
  872.  
  873.     node = node.parentNode;
  874.   }
  875.  
  876.   // Look for a <base>.
  877.   var baseTags = doc.getElementsByTagNameNS(XHTMLNS, "base");
  878.  
  879.   if (baseTags && baseTags.length) 
  880.   {
  881.     urlArr.unshift(baseTags[baseTags.length - 1].getAttribute("href"));
  882.   }
  883.  
  884.   // resolve everything from bottom up, starting with document location
  885.   var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  886.   var URL = ioService.newURI(doc.location.href, null, null);
  887.  
  888.   for (var i=0; i<urlArr.length; i++) 
  889.   {
  890.     URL.spec = URL.resolve(urlArr[i]);
  891.   }
  892.  
  893.   return URL.spec;
  894. }
  895.  
  896. function doCopy(event)
  897. {
  898.   if (!gClipboardHelper) 
  899.     return;
  900.  
  901.   var elem = event.originalTarget;
  902.   if (elem && "treeBoxObject" in elem)
  903.     elem.treeBoxObject.view.performActionOnRow("copy", elem.currentIndex);
  904.  
  905.   var text = elem.getAttribute("copybuffer");
  906.   if (text) 
  907.     gClipboardHelper.copyString(text);
  908. }
  909.  
  910. //******** define a js object to implement nsITreeView
  911. function pageInfoTreeView(columnids, copycol)
  912. {
  913.   // columnids is an array of strings indicating the names of the columns, in order
  914.   this.columnids = columnids;
  915.   this.colcount = columnids.length
  916.   this.copycol = copycol;
  917.   this.rows = 0;
  918.   this.tree = null;
  919.   this.data = new Array;
  920.   this.selection = null;
  921.   this.sortcol = null;
  922.   this.sortdir = 0;
  923. }
  924.  
  925. pageInfoTreeView.prototype = {
  926.   set rowCount(c) { throw "rowCount is a readonly property"; },
  927.   get rowCount() { return this.rows; },
  928.  
  929.   setTree: function(tree) 
  930.   {
  931.     this.tree = tree;
  932.   },
  933.  
  934.   getCellText: function(row, column)
  935.   {
  936.     var colidx = 0;
  937.     // loop through the list of column names to find the index to the column the should be worrying about. very much a hack, but what can you do?
  938.     while(colidx < this.colcount && column != this.columnids[colidx])
  939.       colidx++;
  940.     return this.data[row][colidx] || "";
  941.   },
  942.  
  943.   setCellText: function(row, column, value) 
  944.   {
  945.     var colidx = 0;
  946.     // loop through the list of column names to find the index to the column the should be worrying about. very much a hack, but what can you do?
  947.     while(colidx < this.colcount && column != this.columnids[colidx])
  948.       colidx++;
  949.     this.data[row][colidx] = value;
  950.   },
  951.  
  952.   addRow: function(row)
  953.   {
  954.     var oldrowcount = this.rows;
  955.     this.rows = this.data.push(row);
  956.   },
  957.  
  958.   addRows: function(rows)
  959.   {
  960.     var oldrowcount = this.rowCount;
  961.  
  962.     var length = rows.length;
  963.     for(var i = 0; i < length; i++)
  964.       this.rows = this.data.push(rows[i]);
  965.   },
  966.  
  967.   rowCountChanged: function(index, count)
  968.   {
  969.     this.tree.rowCountChanged(index, count);
  970.   },
  971.  
  972.   invalidate: function()
  973.   {
  974.     this.tree.invalidate();
  975.   },
  976.  
  977.   clear: function()
  978.   {
  979.     this.data = null;
  980.   },
  981.  
  982.   handleCopy: function(row)
  983.   {
  984.     return (row < 0) ? "" : (this.data[row][this.copycol] || "");
  985.   },
  986.  
  987.   performActionOnRow: function(action, row)
  988.   {
  989.     if (action == "copy")
  990.       var data = this.handleCopy(row)
  991.     this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
  992.   },
  993.  
  994.   getRowProperties: function(row, column, prop) { },
  995.   getCellProperties: function(row, prop) { },
  996.   getColumnProperties: function(column, elem, prop) { },
  997.   isContainer: function(index) { return false; },
  998.   isContainerOpen: function(index) { return false; },
  999.   isSeparator: function(index) { return false; },
  1000.   isSorted: function() { },
  1001.   canDropOn: function(index) { return false; },
  1002.   canDropBeforeAfter: function(index, before) { return false; },
  1003.   drop: function(row, orientation) { return false; },
  1004.   getParentIndex: function(index) { return 0; },
  1005.   hasNextSibling: function(index, after) { return false; },
  1006.   getLevel: function(index) { return 0; },
  1007.   getImageSrc: function(row, column) { },
  1008.   getProgressMode: function(row, column) { },
  1009.   getCellValue: function(row, column) { },
  1010.   toggleOpenState: function(index) { },
  1011.   cycleHeader: function(col, elem) { },
  1012.   selectionChanged: function() { },
  1013.   cycleCell: function(row, column) { },
  1014.   isEditable: function(row, column) { return false; },
  1015.   performAction: function(action) { },
  1016.   performActionOnCell: function(action, row, column) { }
  1017. };
  1018.